package com.example.config.shiro;

import com.example.config.CommonConstant;
import com.example.enity.Admin;
import com.example.service.AdminService;
import com.example.util.JwtUtil;
import com.example.util.RedisUtil;
import com.example.util.oConvertUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class ShiroRelam extends AuthorizingRealm {
    @Resource
    private AdminService adminService;

    @Autowired
    private RedisUtil redisUtil;


    /**
     * 必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }


    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 身份验证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String token = (String) authenticationToken.getCredentials();
        Admin admin = checkAdminTokenIsEffect(token);
        return new SimpleAuthenticationInfo(admin, token, getName());
    }

    /**
     * 校验token的有效性
     * @param token
     */
    public Admin checkAdminTokenIsEffect(String token) throws AuthenticationException {
        // 解密获得username，用于和数据库进行对比
        String username = JwtUtil.getUsername(token);
        if (username == null) {
            throw new AuthenticationException("token非法无效!");
        }
        Admin admin = adminService.getAdminByUsername(username);
        if( admin == null ){
            throw new AuthenticationException("用户不存在!");
        }
        //校验token是否超时失效 & 或者账号密码是否错误
        if (!jwtTokenRefresh(token, username, admin.getPwd())) {
            throw new AuthenticationException("Token失效，请重新登录!");
        }
        return admin;

    }

    /**
     * 令牌刷新机制
     * 1.token是否过期取决于redis中的token，而不是Jwt的token
     * 2.当redis中的token过期后，返回false
     * 3.当redis中的token没有过期，Jwt中的token也没有过期，返回true
     * 4.当redis中的token没有过期，Jwt中的token
     * @param token
     * @param username
     * @param pwd
     * @return
     */
    private boolean jwtTokenRefresh(String token, String username, String pwd) {
        String cacheToken = String.valueOf(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));
        if (oConvertUtils.isNotEmpty(cacheToken)) {
            // 校验token有效性:用户名、密码是否正确，token是否过期
            //令牌刷新机制
            if (!JwtUtil.verify(cacheToken, username, pwd)) {
                //无效
                //重新生成token
                String newAuthorization = JwtUtil.sign(username, pwd);
                // 重新设置超时时间
                redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
                redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
            }
            return true;
        }
        return false;
    }

}
